package com.htfyun.factorytest.utils;

import android.content.ContentResolver;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public final class ReflectUtils {

    public static Class<?> getClass(String className) {

        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Class<?> getClass(ClassLoader loader, String className) {

        try {
            if (loader == null) {
                return Class.forName(className);
            }
            return loader.loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Class<?> getClass(Object obj) {
        if (obj instanceof String) {
            return getClass((String) obj);
        } else if (obj instanceof Class<?>) {
            return (Class<?>) obj;
        }
        return obj.getClass();
    }

    public static Object newInstance(Object obj, Object... args) throws Exception {

        Class<?> cla;
        if (obj instanceof Class<?>) {
            cla = (Class<?>) obj;
        } else {
            cla = getClass(obj);
        }
        if (cla == null) {
            return null;
        }
        Class<?>[] argsClass = getArgClass(args);
        if (argsClass != null) {
            Constructor<?> cons = cla.getConstructor(argsClass);
            return cons.newInstance(args);
        } else {
            return cla.newInstance();
        }
    }

    public static boolean isInstance(Object obj, Class<?> cls) {

        return cls.isInstance(obj);
    }

    public static Object cast(Object obj, Class<?> cls) {

        return cls.cast(obj);
    }

    public static boolean isExistClass(String className) {

        return getClass(className) != null;
    }

    public static Object invokeMethodWithNoException(Object owner, String methodName, Object... args) {
        try {
            return invokeMethod(owner, methodName, args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object invokeMethod(Object owner, String methodName, Object... args) throws Exception {

        return invoke(owner, getMethod(owner, methodName, args), args);
    }

    public static Object invokeWithNoException(Object owner, Method method, Object... args) {
        try {
            return invoke(owner, method, args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object invoke(Object owner, Method method, Object... args) throws Exception {

        if (method == null) {
            return null;
        }
        return method.invoke((owner instanceof String) ? null : owner, args);
    }

    public static Method getMethodWithNoException(Object owner, String methodName, Object... args) {
        try {
            return getMethod(owner, methodName, args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Method getMethod(Object owner, String methodName, Object... args) throws Exception {
        Class<?> cla = getClass(owner);
        if (cla == null) {
            return null;
        }
        Class<?>[] argsClass = getArgClass(args);
        Method method = getMethodInternal(cla, methodName, argsClass);
        method.setAccessible(true);
        return method;
    }

    private static Method getMethodInternal(Class<?> owner, String methodName, Class<?>[] args) throws Exception {
        try {
            return (args == null) ? owner.getDeclaredMethod(methodName) : owner.getDeclaredMethod(methodName, args);
        } catch (NoSuchMethodException e) {
            try {
                return (args == null) ? owner.getMethod(methodName) : owner.getMethod(methodName, args);
            } catch (NoSuchMethodException t) {
                Class<?> superCla = owner.getSuperclass();
                if (superCla == null || "java.lang.Object".equals(superCla.getName())) {
                    throw t;
                }
                return getMethodInternal(superCla, methodName, args);
            }
        }
    }

    private static Class<?>[] getArgClass(Object... args) {
        Class<?>[] argsClass = null;
        if (args != null && args.length > 0) {
            argsClass = new Class[args.length];
            for (int i = 0, j = args.length; i < j; i++) {
                argsClass[i] = getBaseClass(args[i]);
            }
        }
        return argsClass;
    }

    private static Class<?> getBaseClass(Object arg) {
        Class<?> c;
        if (arg instanceof Class<?>) {
            c = (Class<?>) arg;
        } else {
            c = arg.getClass();
        }
        String name = getTypeName(c);
        if (Integer.class.getName().equals(name)) {
            return int.class;
        } else if (Float.class.getName().equals(name)) {
            return float.class;
        } else if (Double.class.getName().equals(name)) {
            return double.class;
        } else if (Boolean.class.getName().equals(name)) {
            return boolean.class;
        } else if (Long.class.getName().equals(name)) {
            return long.class;
        } else if (arg instanceof Handler.Callback) {
            return Handler.Callback.class;
        } else if ("android.os.BinderProxy".equals(name)) {
            return IBinder.class;
        } else if (isUseSuperClass(c, name, "android.content.ContentResolver")) {
            return ContentResolver.class;
        } else if (isUseSuperClass(c, name, "android.content.Context")) {
            return Context.class;
        }
        return c;
    }

    private static boolean isUseSuperClass(Class<?> c, String name, String supername) {
        if (c != null && !c.isArray()) {
            if (name.equals(supername)) {
                return true;
            }
            Class<?> superclass = c.getSuperclass();
            while (superclass != null) {
                name = superclass.getName();
                if (name.equals(supername)) {
                    return true;
                } else if ("java.lang.Object".equals(name)) {
                    break;
                } else {
                    superclass = superclass.getSuperclass();
                }
            }
        }
        return false;
    }

    private static String getTypeName(Class<?> c) {
        if (c.isArray()) {
            Class<?> cl = c;
            int dimensions = 0;
            while (cl.isArray()) {
                dimensions++;
                cl = cl.getComponentType();
            }
            StringBuilder sb = new StringBuilder();
            sb.append(cl.getName());
            for (int i = 0; i < dimensions; i++) {
                sb.append("[]");
            }
            return sb.toString();
        }
        return c.getName();
    }

    public static Field getFieldWithNoException(Object obj, String field) {
        try {
            return getField(obj, field);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Field getField(Object obj, String field) throws Exception {
        Class<?> cla = getClass(obj);
        if (cla == null) {
            return null;
        }
        try {
            return cla.getDeclaredField(field);
        } catch (NoSuchFieldException e) {
            try {
                return cla.getField(field);
            } catch (NoSuchFieldException t) {
                Class<?> superCla = cla.getSuperclass();
                if (superCla == null || "java.lang.Object".equals(superCla.getName())) {
                    throw t;
                }
                return getField(superCla, field);
            }
        }
    }

    public static Object getFieldValueWithNoException(Object obj, String field) {
        try {
            return getFieldValue(obj, field);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object getFieldValue(Object obj, String field) throws Exception {

        Class<?> cla = getClass(obj);
        if (cla == null) {
            return null;
        }
        return getFieldValue((obj instanceof String) ? cla : obj, getField(cla, field));
    }

    public static Object getFieldValue(Object obj, Field field) throws Exception {

        field.setAccessible(true);
        return field.get(obj);
    }

    public static void setFieldValue(Object obj, String field, Object value) throws Exception {
        Class<?> cla = getClass(obj);
        if (cla == null) {
            return;
        }
        setFieldValue((obj instanceof String) ? cla : obj, getField(cla, field), value);
    }

    public static void setFieldValue(Object obj, Field field, Object value) throws Exception {

        field.setAccessible(true);
        Class<?> type = field.getType();
        if (type.equals(Integer.class)) {
            field.set(obj, Integer.valueOf(value.toString()));
        } else if (type.equals(String.class)) {
            field.set(obj, value.toString());
        } else if (type.equals(Float.class)) {
            field.set(obj, Float.valueOf(value.toString()));
        } else if (type.equals(Double.class)) {
            field.set(obj, Double.valueOf(value.toString()));
        } else if (type.equals(Boolean.class)) {
            field.set(obj, Boolean.valueOf(value.toString()));
        }
    }
}